#include "stm32f4xx.h"                  // Device header
#include "stm32f4xx_conf.h"
#include "OLED.h"
#include "OLED_Font.h"

/*
 *函数简介:OLED专用us延时
 *参数说明:延时时长,单位us
 *返回类型:无
 *备注:参数范围:0~4294967295
 */
void OLED_Delay_us(uint32_t us)
{
	uint32_t temp;	    	 
	SysTick->LOAD=us*7; 						//时间加载，我们要延时n倍的us, 1us是一个fac_ua周期，所以总共要延时的周期值为二者相乘最后送到Load中。		 
	SysTick->VAL=0x00;        					//清空计数器
	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;	//开启使能位 开始倒数
	do temp=SysTick->CTRL;
	while((temp&0x01) && !(temp&(1<<16)));		//用来判断 systick 定时器是否还处于开启状态，然后在等待时间到达，也就是数到0的时候,此时第十六位设置为1
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; 	//关闭计数器使能位
	SysTick->VAL=0x00;       					//清空计数器
}

/*
 *函数简介:OLED专用ms延时
 *参数说明:延时时长,单位ms
 *返回类型:无
 *备注:参数范围:0~4294967295
 */
void OLED_Delay(uint32_t xms)
{
	while(xms--)
		OLED_Delay_us(1000);
}

/*
 *函数简介:OLED专用指数函数
 *参数说明:底数x
 *参数说明:指数y
 *返回类型:x^y
 *备注:x,y均为整数
 */
uint32_t OLED_Pow(uint32_t x,uint32_t y)
{
	uint32_t Result=1;
	while(y--)Result*=x;
	return Result;
}

/*
 *函数简介:OLED专属软件I2C写SCL电平
 *参数说明:高低电平,0-低电平 1-高电平
 *返回类型:无
 *备注:默认SCL为I2C2_SCL(PF1)
 *备注:若修改引脚,需要到OLED.h文件中修改
 *备注:由于I2C速度较慢,在更改电平之后需要加入延时
 */
void OLED_SCL(uint8_t x)
{
	GPIO_WriteBit(OLED_SCL_GPIOx,OLED_SCL_Pin,(BitAction)(x));
	OLED_Delay_us(1);
}

/*
 *函数简介:OLED专属软件I2C写SDA电平
 *参数说明:高低电平,0-低电平 1-高电平
 *返回类型:无
 *备注:默认SDA为I2C2_SDA(PF0)
 *备注:若修改引脚,需要到OLED.h文件中修改
 *备注:由于I2C速度较慢,在更改电平之后需要加入延时
 */
void OLED_SDA(uint8_t x)
{
	GPIO_WriteBit(OLED_SDA_GPIOx,OLED_SDA_Pin,(BitAction)(x));
	OLED_Delay_us(1);
}

/*
 *函数简介:OLED专属软件I2C初始化
 *参数说明:无
 *返回类型:无
 *备注:默认SCL为I2C2_SCL(PF1),SDA为I2C2_SDA(PF0)
 *备注:若修改引脚,需要到OLED.h文件中修改
 */
void OLED_I2C_Init(void)//OLED专属I2C初始化
{
	RCC_AHB1PeriphClockCmd(OLED_SCL_RCC,ENABLE);//开启SCL时钟
	RCC_AHB1PeriphClockCmd(OLED_SDA_RCC,ENABLE);//开启SDA时钟
	
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType=GPIO_OType_OD;//开漏输出
	GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;//默认上拉
	GPIO_InitStructure.GPIO_Pin=OLED_SCL_Pin;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;
 	GPIO_Init(OLED_SCL_GPIOx,&GPIO_InitStructure);//配置SCL
	GPIO_InitStructure.GPIO_Pin=OLED_SDA_Pin;
 	GPIO_Init(OLED_SDA_GPIOx,&GPIO_InitStructure);//配置SDA
	
	OLED_SCL(1);//初始化SCL
	OLED_SDA(1);//初始化SDA
}

/*
 *函数简介:OLED专属软件I2C起始
 *参数说明:无
 *返回类型:无
 *I2C协议:
 *	SCL	~~~~/---\___
 *	SDA	~~/---\_____
 */
void OLED_I2C_Start(void)
{
	OLED_SDA(1);
	OLED_SCL(1);//复位SCL和SDA
	OLED_SDA(0);//起始
	OLED_SCL(0);//准备接收数据
}

/*
 *函数简介:OLED专属软件I2C终止
 *参数说明:无
 *返回类型:无
 *I2C协议:
 *	SCL	____/-----
 *	SDA	~~\___/---
 */
void OLED_I2C_Stop(void)
{
	OLED_SDA(0);
	OLED_SCL(1);//准备接收停止信号
	OLED_SDA(1);//终止
}

/*
 *函数简介:OLED专属软件I2C接收应答
 *参数说明:无
 *返回类型:无
 *备注:为节省时间,省去接收功能(不处理应答),故不需要释放总线和读取SDA电平
 *I2C协议:
 *	SCL	____/-\___
 *	SDA	~~/-|==|~~
 *			(  )从机
 *修改为:
 *	SCL	____/-\___
 *	SDA	~~~~~~~~~~
 */
void OLED_I2C_ReceiveAck(void)
{
	OLED_SCL(1);//产生时钟
	OLED_SCL(0);
}

/*
 *函数简介:OLED专属软件I2C发送一个字节数据
 *参数说明:8bits发送数据
 *返回类型:无
 *备注:在此函数中包含了接收应答的功能
 *备注:为节省时间,省去接收功能(不处理应答)
 *I2C协议:
 *	SCL	____/-\___/-\___/-\___/-\___/-\___/-\___/-\___/-\___
 *	SDA	~~x=====x=====x=====x=====x=====x=====x=====x=====~~
 */
void OLED_I2C_SendByte(uint8_t Byte)//I2C发送一个字节
{
	uint8_t i;
	for(i=0;i<8;i++)
	{
		OLED_SDA(Byte&(0x80>>i));//发送数据每一位，高位先行
		OLED_SCL(1);//产生时钟
		OLED_SCL(0);
	}
	OLED_I2C_ReceiveAck();//接收应答
}

/*
 *函数简介:OLED写命令
 *参数说明:一字节命令
 *返回类型:无
 *备注:写命令标识符为0x00
 *备注:默认从机地址为OLED_Address(0x78),若修改地址,需要到OLED.h文件中修改
 */
void OLED_WriteCommand(uint8_t Command)
{
	OLED_I2C_Start();//起始
	OLED_I2C_SendByte(OLED_Address);//发送从机地址
	OLED_I2C_SendByte(0x00);//发送写命令标识符
	OLED_I2C_SendByte(Command);//发送命令
	OLED_I2C_Stop();//终止
}

/*
 *函数简介:OLED写数据
 *参数说明:一字节数据
 *返回类型:无
 *备注:写数据标识符为0x40
 *备注:默认从机地址为OLED_Address(0x78),若修改地址,需要到OLED.h文件中修改
 */
void OLED_WriteData(uint8_t Data)
{
	OLED_I2C_Start();//起始
	OLED_I2C_SendByte(OLED_Address);//发送从机地址
	OLED_I2C_SendByte(0x40);//发送写数据标识符
	OLED_I2C_SendByte(Data);//发送数据
	OLED_I2C_Stop();//终止
}

/*
 *函数简介:OLED光标定位
 *参数说明:页地址,范围0~7
 *参数说明:列地址,范围0~15
 *返回类型:无
 *备注:默认从机地址为OLED_Address(0x78),若修改地址,需要到OLED.h文件中修改
 *命令帧:S->OLED_Address->0x00->0xB0|Page->0x00|(Row&0x0F)->0x10|(Row&0xF0)>>4->P
 */
void OLED_SetCursor(uint8_t Page,uint8_t Row)
{
	OLED_I2C_Start();//起始
	OLED_I2C_SendByte(OLED_Address);//发送从机地址
	OLED_I2C_SendByte(0x00);//发送写命令
	OLED_I2C_SendByte(0xB0 | Page);//设置页地址
	OLED_I2C_SendByte(0x00 | (Row & 0x0F));//设置列地址低8位
	OLED_I2C_SendByte(0x10 | (Row & 0xF0)>>4);//设置列地址高8位
	OLED_I2C_Stop();//终止
}

/*
 *函数简介:OLED显示一列8bit
 *参数说明:页地址,范围0~7
 *参数说明:列地址,范围0~15
 *参数说明:一字节数据
 *返回类型:无
 *备注:OLED上每一列从上到下对应Data的b0~b7,为1亮起,为0熄灭
 */
void OLED_Light(uint8_t Page,uint8_t Row,uint8_t Data)
{
	OLED_SetCursor(Page,Row);//OLED光标定位
	OLED_WriteData(Data);//OLED写数据
}

/*
 *函数简介:OLED清屏
 *参数说明:无
 *返回类型:无
 *备注:无
 */
void OLED_Clean(void)
{
	for(uint8_t i=0;i<8;i++)//遍历OLED
	{
		OLED_SetCursor(i,0);//光标定位每一页
		for(uint8_t j=0;j<128;j++)//清空每一列
			OLED_WriteData(0x00);//OLED写数据0x00
	}
}

/*
 *函数简介:OLED初始化
 *参数说明:无
 *返回类型:无
 *备注:默认从机地址为OLED_Address(0x78),若修改地址,需要到OLED.h文件中修改
 */
void OLED_Init(void)
{	
	OLED_I2C_Init();//I2C初始化
	
	OLED_I2C_Start();//起始
	OLED_I2C_SendByte(OLED_Address);//发送从机地址
	OLED_I2C_SendByte(0x00);//发送写命令
	OLED_I2C_SendByte(0xAE);//关闭显示
	OLED_I2C_SendByte(0xD5);//设置显示时钟分频比/振荡器频率
	OLED_I2C_SendByte(0x80);
	OLED_I2C_SendByte(0xA8);OLED_I2C_SendByte(0x3F);//设置多路复用率
	OLED_I2C_SendByte(0xD3);OLED_I2C_SendByte(0x00);//设置显示偏移
	OLED_I2C_SendByte(0x40);//设置显示开始行
	OLED_I2C_SendByte(0xA1);//设置左右方向,0xA1正常 0xA0左右反置
	OLED_I2C_SendByte(0xC8);//设置上下方向,0xC8正常 0xC0上下反置
	OLED_I2C_SendByte(0xDA);OLED_I2C_SendByte(0x12);//设置COM引脚硬件配置
	OLED_I2C_SendByte(0x81);OLED_I2C_SendByte(0xCF);//设置对比度控制
	OLED_I2C_SendByte(0xD9);OLED_I2C_SendByte(0xF1);//设置预充电周期
	OLED_I2C_SendByte(0xDB);
	OLED_I2C_SendByte(0x30);//设置VCOMH取消选择级别
	OLED_I2C_SendByte(0xA4);//设置整个显示打开/关闭
	OLED_I2C_SendByte(0xA6);//设置正常/倒转显示
	OLED_I2C_SendByte(0x8D);OLED_I2C_SendByte(0x14);//设置充电泵
	OLED_I2C_SendByte(0xAF);//开启显示
	OLED_I2C_Stop();//终止
		
	OLED_Clean();//OLED清屏
}

/*
 *函数简介:OLED全局明亮
 *参数说明:开启标志位,1-开启全局明亮,0-关闭全局明亮
 *返回类型:
 *备注:无
 */
void OLED_HightLight(uint8_t Flag)
{
	if(Flag)//开启全局明亮
		OLED_WriteCommand(0xA5);
	else//关闭全局明亮
		OLED_WriteCommand(0xA4);
}

/*
 *函数简介:OLED反白
 *参数说明:开启标志位,1-开启反白,0-关闭反白
 *返回类型:无
 *备注:无
 */
void OLED_Turn(uint8_t Flag)
{
	if(Flag)//开启全局明亮
		OLED_WriteCommand(0xA7);
	else//关闭全局明亮
		OLED_WriteCommand(0xA6);
}

/*
 *函数简介:OLED关闭滚动
 *参数说明:无
 *返回类型:无
 *备注:默认从机地址为OLED_Address(0x78),若修改地址,需要到OLED.h文件中修改
 *命令帧2:S->OLED_Address->0x00->0x2E->P
 */
void OLED_ScrollClose(void)
{
	OLED_I2C_Start();//起始
	OLED_I2C_SendByte(OLED_Address);//发送从机地址
	OLED_I2C_SendByte(0x00);//发送写命令
	OLED_I2C_SendByte(0x2E);//关闭滚动
	OLED_I2C_Stop();//终止
}

/*
 *函数简介:OLED滚动
 *参数说明:滚动方向
 *参数说明:起始页,范围1~8
 *参数说明:终止页(终止页>=起始页),范围1~8
 *参数说明:滚动速度0~7,越来越快
 *返回类型:无
 *备注:默认从机地址为OLED_Address(0x78),若修改地址,需要到OLED.h文件中修改
 *命令帧:S->OLED_Address->0x00->0x2E->0x26/0x27->0x00(NULL)->StartPage-1->Speed->EndPage-1->0x00(NULL)->0x00(NULL)->0x2F->P
 */
void OLED_Scroll(OLED_Direction Direction,uint8_t StartPage,uint8_t EndPage,uint8_t Speed)
{
	OLED_I2C_Start();//起始
	OLED_I2C_SendByte(OLED_Address);//发送从机地址
	OLED_I2C_SendByte(0x00);//发送写命令
	OLED_I2C_SendByte(0x2E);//关闭滚动
	OLED_I2C_SendByte(Direction?0x26:0x27);//设置滚动方向
	OLED_I2C_SendByte(0x00);//空字符
	OLED_I2C_SendByte(StartPage-1);//设置起始页
	OLED_I2C_SendByte(Speed);//设置滚动速度
	OLED_I2C_SendByte(EndPage-1);//设置终止页
	OLED_I2C_SendByte(0x00);//空字符
	OLED_I2C_SendByte(0xFF);//空字符
	OLED_I2C_SendByte(0x2F);//开启滚动
	OLED_I2C_Stop();//终止
}

/*
 *函数简介:OLED滚动固定像素
 *参数说明:滚动方向
 *参数说明:起始页,范围1~8
 *参数说明:终止页(终止页>=起始页),范围1~8
 *参数说明:滚动长度(像素数)
 *返回类型:无
 *备注:默认从机地址为OLED_Address(0x78),若修改地址,需要到OLED.h文件中修改
 *备注:测试得到位移一个像素大概21ms
 *命令帧1:S->OLED_Address->0x00->0x2E->0x26/0x27->0x00(NULL)->StartPage-1->0x07->EndPage-1->0x00(NULL)->0x00(NULL)->0x2F->P
 *命令帧2:S->OLED_Address->0x00->0x2E->P
 */
void OLED_ScrollLen(OLED_Direction Direction,uint8_t StartPage,uint8_t EndPage,uint8_t Length)
{
	OLED_I2C_Start();//起始
	OLED_I2C_SendByte(OLED_Address);//发送从机地址
	OLED_I2C_SendByte(0x00);//发送写命令
	OLED_I2C_SendByte(0x2E);//关闭滚动
	OLED_I2C_SendByte(Direction?0x26:0x27);//设置滚动方向
	OLED_I2C_SendByte(0x00);//空字符
	OLED_I2C_SendByte(StartPage-1);//设置起始页
	OLED_I2C_SendByte(0x07);//设置滚动速度最快
	OLED_I2C_SendByte(EndPage-1);//设置终止页
	OLED_I2C_SendByte(0x00);//空字符
	OLED_I2C_SendByte(0xFF);//空字符
	OLED_I2C_SendByte(0x2F);//开启滚动
	OLED_I2C_Stop();//终止
	OLED_Delay(21*Length);//延时
	OLED_I2C_Start();//起始
	OLED_I2C_SendByte(OLED_Address);//发送从机地址
	OLED_I2C_SendByte(0x00);//发送写命令
	OLED_I2C_SendByte(0x2E);//关闭滚动
	OLED_I2C_Stop();//终止
}

/*
 *函数简介:OLED显示字符
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:显示字符
 *返回类型:无
 *备注:字体在OLED_Font.h中定义,若要使用自定义字体,需要到OLED_Font.h中修改
 */
void OLED_ShowChar(uint8_t Line,uint8_t Column,char Char)
{
	OLED_SetCursor((Line-1)*2,(Column-1)*8);//定位上半部分
	for(int i=0;i<8;i++)
	OLED_WriteData(OLED_ASCII[Char-' '][i]);//显示字符上半部分
	OLED_SetCursor((Line-1)*2+1,(Column-1)*8);//定位下半部分
	for(int i=0;i<8;i++)
		OLED_WriteData(OLED_ASCII[Char-' '][i+8]);//显示字符下半部分
}

/*
 *函数简介:OLED显示字符串
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:字符串
 *返回类型:无
 *备注:只能单行显示,溢出部分会被丢弃
 */
void OLED_ShowString(uint8_t Line,uint8_t Column,char *String)
{
	for(uint8_t i=0;String[i];i++)
		if(Column+i<=16)//单行限制
			OLED_ShowChar(Line,Column+i,String[i]);//显示每一个字符
		else break;
}

/*
 *函数简介:OLED显示可换行字符串
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:字符串
 *返回类型:无
 *备注:可换行显示,但溢出OLED的部分会被丢弃
 */
void OLED_ShowLinesString(uint8_t Line,uint8_t Column,char *String)
{
	uint32_t Delta=0;//换行增补值
	for(uint32_t i=0;String[i];i++)
	{
		if(Line+Delta/16==4 && Column+i-Delta>16)//OLED满屏限制
			break;
		if(Column+i-Delta>16)//换行
			Delta+=16;
		OLED_ShowChar(Line+Delta/16,Column+i-Delta,String[i]);//显示每一个字符
	}
}

/*
 *函数简介:OLED显示无符号整数
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:非负整数
 *参数说明:显示长度
 *返回类型:无
 *备注:当显示长度大于整数位数,高位补零;当显示长度小于整数位数,低位舍弃
 */
void OLED_ShowNum(uint8_t Line,uint8_t Column,uint32_t Number,uint8_t Length)
{
	for(uint8_t i=0;i<Length;i++)
		OLED_ShowChar(Line,Column+i,Number/OLED_Pow(10,Length-i-1)%10+'0');//显示每一位
}

/*
 *函数简介:OLED显示变长度无符号整数
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:非负整数
 *返回类型:无
 *备注:可以根据整数自主获取整数位数
 */
void OLED_ShowVarLenNum(uint8_t Line,uint8_t Column,uint32_t Number)
{
	uint8_t Length;
	for(Length=1;;Length++)//获得整数长度
		if(Number<OLED_Pow(10,Length))break;
	OLED_ShowNum(Line,Column,Number,Length);//显示每一位
}

/*
 *函数简介:OLED显示有符号整数
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:整数
 *参数说明:显示长度
 *返回类型:无
 *备注:最终显示会比Length多一位符号位
 *备注:当显示长度大于整数位数,高位补零;当显示长度小于整数位数,低位舍弃
 */
void OLED_ShowSignedNum(uint8_t Line,uint8_t Column,int32_t Number,uint8_t Length)
{
	if(Number<0)//显示符号
	{
		OLED_ShowChar(Line,Column,'-');
		Number=-Number;
	}
	else
		OLED_ShowChar(Line,Column,'+');
	OLED_ShowNum(Line,Column+1,Number,Length);//显示绝对值
}

/*
 *函数简介:OLED显示变长度有符号整数
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:整数
 *返回类型:无
 *备注:最终显示会比整数位数多一位符号位
 *备注:可以根据整数自主获取整数位数
 */
void OLED_ShowSignedVarLenNum(uint8_t Line,uint8_t Column,int32_t Number)
{
	if(Number<0)//显示符号
	{
		OLED_ShowChar(Line,Column,'-');
		Number=-Number;
	}
	else
		OLED_ShowChar(Line,Column,'+');
	OLED_ShowVarLenNum(Line,Column+1,Number);//显示绝对值
}

/*
 *函数简介:OLED显示十六进制整数
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:非负整数
 *参数说明:显示长度
 *返回类型:无
 *备注:OLED上以十六进制显示
 *备注:当显示长度大于十六进制位数,高位补零;当显示长度小于十六进制位数,低位舍弃
 */
void OLED_ShowHexNum(uint8_t Line,uint8_t Column,uint32_t Number,uint8_t Length)
{
	for(uint8_t i=0;i<Length;i++)
	{
		uint8_t HexNum=Number/OLED_Pow(16,Length-i-1)%16;//转换十六进制
		if(HexNum>=10)//显示
			OLED_ShowChar(Line,Column+i,HexNum-10+'A');
		else
			OLED_ShowChar(Line,Column+i,HexNum+'0');
	}
}

/*
 *函数简介:OLED显示八进制整数
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:非负整数
 *参数说明:显示长度
 *返回类型:无
 *备注:OLED上以八进制显示
 *备注:当显示长度大于八进制位数,高位补零;当显示长度小于八进制位数,低位舍弃
 */
void OLED_ShowOctNum(uint8_t Line,uint8_t Column,uint32_t Number,uint8_t Length)
{
	for(uint8_t i=0;i<Length;i++)//转换八进制并显示
		OLED_ShowChar(Line,Column+i,Number/OLED_Pow(8,Length-i-1)%8+'0');
}

/*
 *函数简介:OLED显示二进制整数
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:非负整数
 *参数说明:显示长度
 *返回类型:无
 *备注:OLED上以二进制显示
 *备注:当显示长度大于二进制位数,高位补零;当显示长度小于二进制位数,低位舍弃
 */
void OLED_ShowBinNum(uint8_t Line,uint8_t Column,uint32_t Number,uint8_t Length)
{
	for(uint8_t i=0;i<Length;i++)//转换二进制并显示
		OLED_ShowChar(Line,Column+i,Number/OLED_Pow(2,Length-i-1)%2+'0');
}

/*
 *函数简介:OLED显示小数
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:非负浮点数
 *参数说明:整数长度
 *参数说明:小数长度
 *返回类型:无
 *备注:最终显示长度为IntLen+DecLen+1
 *备注:由于浮点数存储问题,会出现一定问题,例如0.1变成0.09999999999...
 *备注:当显示长度大于整数位数,高位补零;当显示长度小于整数位数,低位舍弃
 *备注:当显示长度大于小数位数,低位补零;当显示长度小于小数位数,低位舍弃
 */
void OLED_ShowDecimal(uint8_t Line,uint8_t Column,float Number,uint8_t IntLen,uint8_t DecLen)
{
	OLED_ShowNum(Line,Column,(uint32_t)Number,IntLen);//显示整数
	OLED_ShowChar(Line,Column+IntLen,'.');//显示小数点
	Number=(Number-(uint32_t)Number)*OLED_Pow(10,DecLen);//获取小数部分(由于浮点数存储问题,会出现一定问题,例如0.1变成0.09999999999...)
	OLED_ShowNum(Line,Column+IntLen+1,(uint32_t)Number,DecLen);//显示小数
}

/*
 *函数简介:OLED显示变整数长度小数
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:非负浮点数
 *参数说明:小数长度
 *返回类型:无
 *备注:最终显示长度为IntLen+DecLen+1
 *备注:可以根据整数自主获取整数位数
 *备注:由于浮点数存储问题,会出现一定问题,例如0.1变成0.09999999999...
 *备注:当显示长度大于小数位数,低位补零;当显示长度小于小数位数,低位舍弃
 */
void OLED_ShowVarIntLenDecimal(uint8_t Line,uint8_t Column,float Number,uint8_t DecLen)
{
	uint8_t IntLen;
	for(IntLen=1;;IntLen++)//获取整数长度
		if((uint32_t)Number<OLED_Pow(10,IntLen))
			break;
	OLED_ShowNum(Line,Column,(uint32_t)Number,IntLen);//显示整数
	if(DecLen!=0)
	{
		OLED_ShowChar(Line,Column+IntLen,'.');//显示小数点
		Number=(Number-(uint32_t)Number)*OLED_Pow(10,DecLen);//获取小数部分(由于浮点数存储问题,会出现一定问题,例如0.1变成0.09999999999...)
		OLED_ShowNum(Line,Column+IntLen+1,(uint32_t)Number,DecLen);//显示小数
	}
}

/*
 *函数简介:OLED显示变长度小数
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:非负浮点数
 *返回类型:无
 *备注:最终显示长度为IntLen+DecLen+1
 *备注:可以根据整数自主获取整数位数,根据小数自主获取小数位数
 *备注:由于浮点数存储问题,会出现一定问题,例如0.1变成0.09999999999...
 */
void OLED_ShowVarLenDecimal(uint8_t Line,uint8_t Column,float Number)
{
	uint8_t IntLen;
	for(IntLen=1;;IntLen++)//获取整数长度
		if((uint32_t)Number<OLED_Pow(10,IntLen))
			break;
	OLED_ShowNum(Line,Column,(uint32_t)Number,IntLen);//显示整数
	Number=Number-(uint32_t)Number;
	uint8_t DecLen;
	for(DecLen=0;DecLen<6;DecLen++)//获取小数位数,同时获取小数部分(由于浮点数存储问题,会出现一定问题,例如0.1变成0.09999999999...)
	{
		if(Number-(uint32_t)Number<1e-4f && (uint32_t)Number-Number<1e-4f)
			break;
		Number*=10.0f;
	}
	if(DecLen!=0)
	{
		OLED_ShowChar(Line,Column+IntLen,'.');//显示小数点
		OLED_ShowNum(Line,Column+IntLen+1,(uint32_t)Number,DecLen);//显示小数
	}
}

/*
 *函数简介:OLED显示有符号小数
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:浮点数
 *参数说明:整数长度
 *参数说明:小数长度
 *返回类型:无
 *备注:最终显示长度为IntLen+DecLen+2,最前方多一位符号位
 *备注:由于浮点数存储问题,会出现一定问题,例如0.1变成0.09999999999...
 *备注:当显示长度大于整数位数,高位补零;当显示长度小于整数位数,低位舍弃
 *备注:当显示长度大于小数位数,低位补零;当显示长度小于小数位数,低位舍弃
 */
void OLED_ShowSignedDecimal(uint8_t Line,uint8_t Column,float Number,uint8_t IntLen,uint8_t DecLen)
{
	if(Number<0)//显示符号
	{
		OLED_ShowChar(Line,Column,'-');
		Number=-Number;
	}
	else
		OLED_ShowChar(Line,Column,'+');
	OLED_ShowDecimal(Line,Column+1,Number,IntLen,DecLen);//显示绝对值
}

/*
 *函数简介:OLED显示变整数长度有符号小数
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:浮点数
 *参数说明:小数长度
 *返回类型:无
 *备注:最终显示长度为IntLen+DecLen+2,最前方多一位符号位
 *备注:可以根据整数自主获取整数位数
 *备注:由于浮点数存储问题,会出现一定问题,例如0.1变成0.09999999999...
 *备注:当显示长度大于小数位数,低位补零;当显示长度小于小数位数,低位舍弃
 */
void OLED_ShowSignedVarIntLenDecimal(uint8_t Line,uint8_t Column,float Number,uint8_t DecLen)
{
	if(Number<0)//显示符号
	{
		OLED_ShowChar(Line,Column,'-');
		Number=-Number;
	}
	else
		OLED_ShowChar(Line,Column,'+');
	OLED_ShowVarIntLenDecimal(Line,Column+1,Number,DecLen);//显示绝对值
}

/*
 *函数简介:OLED显示变长度有符号小数
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~16
 *参数说明:浮点数
 *返回类型:无
 *备注:最终显示长度为IntLen+DecLen+2,最前方多一位符号位
 *备注:可以根据整数自主获取整数位数,根据小数自主获取小数位数
 *备注:由于浮点数存储问题,会出现一定问题,例如0.1变成0.09999999999...
 */
void OLED_ShowSignedVarLenDecimal(uint8_t Line,uint8_t Column,float Number)
{
	if(Number<0)//显示符号
	{
		OLED_ShowChar(Line,Column,'-');
		Number=-Number;
	}
	else
		OLED_ShowChar(Line,Column,'+');
	OLED_ShowVarLenDecimal(Line,Column+1,Number);//显示绝对值
}


/*
 *函数简介:OLED显示中文(未完待续)
 *参数说明:行坐标,范围1~4
 *参数说明:列坐标,范围1~8
 *返回类型:中文UTF-8字符
 *备注:无
 */
void OLED_ShowChinese(uint8_t Line,uint8_t Column,char *Chinese)
{
	for(int i=0;Chinese[i];i+=3)//中文为三位字节编码16x16像素，例如“一”对应E4 B8 80
	{
		OLED_SetCursor((Line-1)*2,(Column+i/3-1)*16);
		for(int j=0;j<16;j++)
			OLED_WriteData(OLED_Chinese[Chinese[i+2]-0x80][j]);
		OLED_SetCursor((Line-1)*2+1,(Column+i/3-1)*16);
		for(int j=0;j<16;j++)
			OLED_WriteData(OLED_Chinese[Chinese[i+2]-0x80][j+16]);
	}
}

/*
 *函数简介:OLED显示函数图像
 *参数说明:无
 *返回类型:无
 *备注:默认原点4页64列b7,xy轴单位长度为0.1,函数为sinx
 *备注:函数配置在OLED.h文件中,若要修改,需要到OLED.h文件中修改
 */
void OLED_ShowFunction(void)
{
	for(uint8_t j=0;j<8;j++)//显示Y轴
		OLED_Light(j,X0,0xFF);
	for(int16_t i=0;i<128;i++)
	{
		int16_t Delta=(int16_t)((f((i-X0)*unitX))/unitY);//计算每一列像素点的位置
		if(Delta>=0)//X轴上方
		{
			if(Y0-Delta/8<0)//越界
			{
				OLED_Light(Y0,i,0x80);
				continue;
			}
			OLED_SetCursor(Y0-Delta/8,i);//定位
			if(i==X0)
				OLED_WriteData(~(0x80>>(Delta%8)));//显示函数与Y轴交点（在Y轴上涂黑）
			else//显示像素点
				if(Delta<8)//与X轴在同一页
					OLED_WriteData((0x80>>(Delta%8))|0x80);
				else
				{
					OLED_WriteData(0x80>>(Delta%8));
					OLED_Light(Y0,i,0x80);
				}
		}
		else//X轴下方
		{
			Delta=-Delta;
			if(Y0+(Delta+7)/8>7)//越界
			{
				OLED_Light(Y0,i,0x80);
				continue;
			}
			OLED_SetCursor(Y0+(Delta+7)/8,i);//定位
			if(i==X0)//显示函数与Y轴交点（在Y轴上涂黑）
				OLED_WriteData(~(0x01<<((Delta-1)%8)));
			else//显示像素点
				OLED_WriteData(0x01<<((Delta-1)%8));
				OLED_Light(Y0,i,0x80);
		}
	}
}

/*
 *函数简介:OLED显示图片
 *参数说明:无
 *返回类型:无
 *备注:图片大小64*128
 *备注:图片内容在OLED_Font.h文件,若要修改,需要到OLED_Font.h文件中修改
 */
void OLED_ShowPicture(void)
{
	for(uint8_t i=0;i<8;i++)
	{
		OLED_SetCursor(i,0);
		for(uint8_t j=0;j<128;j++)
			OLED_WriteData(OLED_Picture[i*128+j]);
	}
}
